/* 
Copyright (c) 2004,2005, 2006, 2007, 2008 University of Illinois 

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* Objects and related functions for DHTML/Widgets
 * romanows, 3/10/06
 */
AccessibilityExtension.ns(function() { with (AccessibilityExtension.LIB) {

	var AE = AccessibilityExtension.ns;  
	AE.validStateValuesArray = new Array();
	AE.validPropertyValuesArray = new Array();
	
	/**
	 * validNameValuePair should only be used with arrays contains dtdTripleObject(s).
	 * @param {searchName} 
	 * @param {searchValue}  
	 * @return boolean true if the the searchValue is a possible value associated with the searchName
	 * @author Sandy Foltz
	*/
	Array.prototype.validNameValuePair = function(searchName, searchValue) {
	  for (i=0; i<this.length; i++) {
	      if (this[i].aaaname==searchName) {
	 		if (this[i].aaavalue.toLowerCase().indexOf(searchValue.toLowerCase()) >= 0) {
				return true;
			} else {
				return this[i].aaavalue;
			}
	      }
	   }
	  return null; 
	}
	Array.prototype.validName = function(searchName) {
		  for (i=0; i<this.length; i++) {if (this[i].aaaname==searchName) return true; }
		  return false; 
	}
	
	function aaaStateObjectDisplayDetails() {}
	aaaStateObjectDisplayDetails.prototype = {
		dataElementNames : ['order','state','value'],
		columnHeaders : ['column.header.order.label','column.header.state.label','column.header.value.label'],
			
		accessIssuesDataElementNames : [],
		accessIssuesMessages : [],
	}
	var aaaStateObjectDisplayDetails = new aaaStateObjectDisplayDetails();
	
	// Call with an element's state attribute node OR with a state/value string pair like "aaaStateObject(state,value)"
	function aaaStateObject(attribute) {
	   if(arguments.length<=1) {
	      assert(isStateAttribute(attribute));
	      this.parseAttribute(attribute);
	   } else {
	      if(arguments.length==2) {
	         this.state = arguments[0];
	         this.value = arguments[1];
	      }
	   }
	}
	aaaStateObject.prototype = {
	   order : 0,
	   state : "",
	   value : "",
	   lastValue : "",
	   display : true,
	   supportedState : true,
	   validAttributeValue : null,  //null=NA, true=is valid, <value>=is invalid (warn user)
	   invalidIDREF : null,			//null=NA, <value>=the invalid reference
	   emptyIDREF : false,
	   toomanyIDREF : false,
	   display : true,
	   displayDetails: aaaStateObjectDisplayDetails,
	   allStates : [        
	         'checked',
	         'iconed',
	         'disabled',
	         'readonly',
	         'multiselectable',
	         'domactive',
	         'zoom',
	         'expanded',
	         'selected',
	         'pressed',
	         'important',
	         'required',
	         'haseffect',
	         'valuenow',
	         'valuemax',
	         'valuemin',
	         'step',
	         'invalid',
	         'describedby',
	         'labeledby',
	         'hasparent',
	         'haschild',
	         'haspopup',
	         'alternatestyle',
	         'tabindex',
	         'flowto',
	         'flowfrom',
	         'controls',
	         'controlledby',
	         'subpageof'],
	   hasChanged: false, // set when updateState detects a change, user may clear
	
	   parseAttribute : function(attribute) { // sets this.state and this.value given an aaa state attribute
	   	if (attribute.localName.substring(0, 5) != 'aria-') {
			this.state = attribute.localName;
		}else {
			this.state = attribute.localName.substring(5);		
		}
	       
	      this.value = attribute.value;
	   },
		setRowColor : function (item) {
		  var row = item.getElementsByTagName('treerow');
	  	  if (currStandard() == 'aria') {
		  	if (this.supportedState == false || (this.validAttributeValue != null && this.validAttributeValue != true ) || this.invalidIDREF != null || this.emptyIDREF || this.toomanyIDREF) {
				row[0].setAttribute('properties','warnRow');
			}
		  }	
		},
	   getTreeNode : function(windoc) {
	   	  var item = null;
	      item = accesxt_generateTreeboxResults(windoc,this);
		  this.setRowColor(item);
		  return item;
	   },
	   isSameState : function(stateObject) { // true if two state objects are equal (but not necessarily identical)
	      if( (this.state == stateObject.state) ) {
	         return(true);
	      }
	      return(false);
	   },
	   isEqual : function(stateObject) { // true if two state objects are equal (but not necessarily identical)
	      if( this.isSameState(stateObject) && (this.value == stateObject.value) ) {
	         return(true);
	      }
	      return(false);
	   },
	   // NOTE: This isn't used in the code, probably take it out
	   // Finds the appropriate state attribute for this object in the given node, 
	   // ... updates this object's information if it has changed and returns true, 
	   // ... or returns false if no update is needed.
	   updateState : function(node) { 
	      // Get the node's array of states
	      states = getStateArray(node, "xxxrole");
	      for( s in states ) {
	         // for each stateObject in the array, compare the stateName to this object
	         if( this.isSameState(s) ) {
	            if( this.isEqual(s) ) {
	               return(false); // nothing new
	            } else {
	               // "push" this state
	               this.lastValue = this.value
	               this.value = s.value;
	               this.hasChanged = true;
	               return(true);
	            }
	         }
	      }
	      // If the state cannot be found and this.value is not null, then set this.value=null, push this state, update hasChanged, and return true;
	      if( this.value != "" ) {
	         this.lastValue = this.value;
	         this.value = "";
	         this.hasChanged = true;
	         return(true);
	      }
	   },
	   // True if this state is a state specified by the standard it may not necessarily be a supportedState for the widget though.
	   isStandardState : function() {
	      for(var i=0; i<this.allStates.length; i++) {
	         if(this.state == this.allStates[i]) {
	            return true;
	         } 
	      }
	      return false;
	   }
	}
	
	function widgetObjectDisplayDetails() {}
	widgetObjectDisplayDetails.prototype = {
		dataElementNames : ['icita07', 'icita08', 'order', 'baseuri','id','role','tabIndex','hasChanged','watch'],
		columnHeaders : ['column.header.icita07.label','column.header.icita08.label','column.header.order.label','column.header.baseuri.label','column.header.widget.label','column.header.role.label','column.header.tabIndex.label', 'column.header.hasChanged.label', 'column.header.watch.label'],
		
		accessIssuesDataElementNames : ['unsupportedstate', 'invalidAttributeValue', 'invalidIDREF', 'emptyIDREF', 'toomanyIDREF'],
		accessIssuesAttrNames : [],
		accessIssuesMessages : ['ariawidget.warnings.unsupportedstate', 'ariawidget.warnings.invalidAttributeValue', 'ariawidget.warnings.invalidIDREF', 'ariawidget.warnings.emptyIDREF', 'ariawidget.warnings.toomanyIDREF'],
	
		accessIssuesUseObjectFunctions : true,
	}
	var widgetObjectDisplayDetails = new widgetObjectDisplayDetails();
	
	function widgetObject(torder,tnode) {
	   this.order = torder;
	   this.parseNode(tnode);
	}
	widgetObject.prototype = {
	   fromformname: '',    // used in the form view 
	   label : false,		// used in the form view 
	   text : '',			// used in the form view 
	   displayOrder : '',	// used in the form view 
	   source : 'NA',		// used in the form view 
	   sourceText : '',		// used in the form view 
	   useAltHeadings : false, // used in the form view 
	   
	   node : null,
	   valid : false,       // FIXME: what is this?
	   id : "",
	   title : "",
	   role : "",
	   tabIndex : "",
	   hasChanged : false,
	   hasState: false,
	   states : null,
	   		//set if any of the role's states have an issue
		   unsupportedstate : false,
		   invalidAttributeValue : false,
		   invalidIDREF: false,
		   emptyIDREF : false,
		   toomanyIDREF : false,
	   watch : false,
	   icita07 : "",
	   icita08 : "",
	   order : false,
	   baseuri : "",
	   display : true,
	   displayDetails: widgetObjectDisplayDetails,
	   // Returns the state object that is named "state" or null if it doesn't exist
	   getState : function(state) {
	      for(var i=0; i<this.states.length; i++) {
	         if(this.states[i].state == state) {
	            return this.states[i];
	         }
	      }
	      return null;
	   },
		/**
		 * Retrieve a particular state (attribute) that correspons to a specific row in the widget list.
		 * @param {findex}  the row in the widget list in which we are to search
		 * @param {targetState}  the state we want to retreive the value of
		 * @return nothing.
		 * @author Sandy Foltz
		*/
		getStateValueInPage : function (targetState) {
		   var usefirstidonly = false;
			
		   // Form the string of states
		   var idStr = "";
		   var nodetext = "";
		   if( this.states.length > 0 ) {
		      for(var i=0; i<this.states.length && idStr == ""; i++) {
			  	if (this.states[i].state == targetState) {
		       		idStr = this.states[i].value;
					if (this.states[i].toomanyIDREF) {usefirstidonly = true;};
				}
		      }
		   }
		   if (idStr != "") {
		   		var returnArray = getTextAssociatedWithIDs(AE.mydoc, idStr, usefirstidonly);
				nodetext = returnArray[0];
		   }
		   return nodetext;
		},
	   parseNode : function(node) {
	      this.display = true; // new duplicate removal
	 	  this.baseuri = getBaseURI(node);
	      this.node = node;
	
	      this.id = GetValueFromAttributes(this.node,['id'],"");
	      this.title = GetValueFromAttributes(this.node,['title'],"");
	      if(this.title=='') { this.title = this.id; }
	      this.tabIndex = GetValueFromAttributes(this.node,['tabindex'],"");
	
		  this.role = readRole(this.node);
		  
	      // Get the node's states
	      this.states = getStateArray(this.node, this.role);
	      	for(var i=0; i<this.states.length; i++) {
				if (this.states[i].supportedState == false) {
					this.unsupportedstate = true; 
				}
				//invalidAttributeValue=Warn: %1 attribute must use %2.
				if (this.states[i].validAttributeValue != null && this.states[i].validAttributeValue != true) {
					this.invalidAttributeValue = true; 
				}
				//invalidIDREF=Warn: The ID %1 referred to by attribute %2 was not found on this page.
				if (this.states[i].invalidIDREF != null) {
					this.invalidIDREF = true; 
				}
				//emptyIDREF=Warn: The attribute %1 is empty.
				if (this.states[i].emptyIDREF) {
					this.emptyIDREF = true; 
				}
				//toomanyIDREF=Warn: The attribute %1 supports only one ID, you have indicated more than one ID.
				if (this.states[i].toomanyIDREF) {
					this.toomanyIDREF = true; 
				}
			}
		  
			//	setMaxSeverityForArray(loadArray);
			this.icita07 = getMaxSeverity(this, 'icita07');		
			this.icita08 = getMaxSeverity(this, 'icita08');		
	    },
		getTreeItem : function(doc) { // this function is used by the list of forms
			var item = accesxt_generateTreeboxResults(doc,this);
			item.setAttribute('hidden',false == this.display);
			return item;
		},
	   getTreeNode : function(windoc) { // this function is used by List of ARIA Widgets
	   	  var item = null;
		  var foundStateIssue = false;
	      item = accesxt_generateTreeboxResults(windoc,this);
	
		  var row = item.getElementsByTagName('treerow');
	      for(var i=0; i<this.states.length && foundStateIssue == false; i++) {
			if (this.states[i].supportedState == false || (this.states[i].validAttributeValue != null && this.states[i].validAttributeValue != true ) || this.states[i].invalidIDREF != null || this.states[i].emptyIDREF || this.states[i].toomanyIDREF) {
				foundStateIssue = true;
			}
		  }
		  
		  if (foundStateIssue == true) {
	 	  	if (currStandard() == 'aria') {
				row[0].setAttribute('properties','warnRow');
			}
		  }	
		  return item;
	   },
		setRowColor : function (item) {
		  var row = item.getElementsByTagName('treerow');
	 	  if (currStandard() == 'aria') {
		    if (this.unsupportedstate || this.invalidIDREF || this.invalidIDREF || this.emptyIDREF || this.toomanyIDREF) {
				row[0].setAttribute('properties','warnRow');
			}
		  }	
		},
	   getNoteText : function(inStd) {
			var note = '';
			var tmp = '';
			var currStd = currStandard();
			if (inStd != undefined) currStd = inStd;
			var notApplicable = accessext_getBundleSingleString('msg.unenforced');
	      	for(var i=0; i<this.states.length; i++) {
				if (this.states[i].supportedState == false) {
					var severity = getErrMsgSeverity('ariawidget.warnings.unsupportedstate', currStd);
					if (severity != notApplicable) {
						tmp = accessext_getBundleString('ariawidget.warnings.unsupportedstate', ['aria-' + this.states[i].state, this.role]);
						note += severity + ': ' + tmp + ' \n'; 					
						this.displayDetails.accessIssuesAttrNames.push('aria-' + this.states[i].state);
					}
				}
				//invalidAttributeValue=Warn: %1 attribute must use %2.
				if (this.states[i].validAttributeValue != null && this.states[i].validAttributeValue != true) {
					var severity = getErrMsgSeverity('ariawidget.warnings.invalidAttributeValue', currStd);
					if (severity != notApplicable) {
						tmp = accessext_getBundleString('ariawidget.warnings.invalidAttributeValue', ['aria-' + this.states[i].state, this.states[i].validAttributeValue]);
						note += severity + ': ' + tmp + ' \n'; 
						this.displayDetails.accessIssuesAttrNames.push('aria-' + this.states[i].state);
					}
				}
				//invalidIDREF=Warn: The ID %1 referred to by attribute %2 was not found on this page.
				if (this.states[i].invalidIDREF != null) {
					var severity = getErrMsgSeverity('ariawidget.warnings.invalidIDREF', currStd);
					if (severity != notApplicable) {
						tmp = accessext_getBundleString('ariawidget.warnings.invalidIDREF', ['aria-' + this.states[i].invalidIDREF, this.states[i].state]);
						note += severity + ': ' + tmp + ' \n'; 
						this.displayDetails.accessIssuesAttrNames.push('aria-' + this.states[i].state);
					}
				}
				//emptyIDREF=Warn: The attribute %1 is empty.
				if (this.states[i].emptyIDREF) {
					var severity = getErrMsgSeverity('ariawidget.warnings.emptyIDREF', currStd);
					if (severity != notApplicable) {
						tmp = accessext_getBundleString('ariawidget.warnings.emptyIDREF', ['aria-' + this.states[i].state]);
						note += severity + ': ' + tmp + ' \n'; 
						this.displayDetails.accessIssuesAttrNames.push('aria-' + this.states[i].state);
					}
				}
				//toomanyIDREF=Warn: The attribute %1 supports only one ID, you have indicated more than one ID.
				if (this.states[i].toomanyIDREF) {
					var severity = getErrMsgSeverity('ariawidget.warnings.toomanyIDREF', currStd);
					if (severity != notApplicable) {
						tmp = accessext_getBundleString('ariawidget.warnings.toomanyIDREF', ['aria-' + this.states[i].state]);
						note += severity + ': ' + tmp + ' \n'; 
						this.displayDetails.accessIssuesAttrNames.push('aria-' + this.states[i].state);
					}
				}
	
			}
			return note;
		},
		listDetailsInHTMLFormat : function () {
			return accesxt_generateHTMLTable('States', this.states);
		},
		clone : function() {
	      var newThis = new Object();
	      for(i in this) {
	         newThis[i] = this[i];
	      }
	      return(newThis);
	   }
	}

	function wairoleObject(id, supportedStates, subClassOf, mustContain, relatedConcept, description) {
		this.id = id;
		this.supportedStates = supportedStates;
		this.subClassOf = subClassOf;
		this.mustContain = mustContain;
		this.relatedConcept = relatedConcept;
		this.description = description;
	}
	wairoleObject.prototype = {
		id : '',
		supportedStates : [],
		subClassOf : [],
		mustContain : [],
		relatedConcept : [],
		description : ''
	}
	
AccessibilityExtension.LIB.extend(AccessibilityExtension.LIB,
{
	newWidgetObject: function(torder, tnode) { 
		var n = new widgetObject(torder, tnode);
		return n; 
	},
	
	newaaaStateObject: function(attribute) {
		var n = new aaaStateObject(attribute);
		return n; 
	},

	// Applies an event listener to some content document-type node and all sub-frames/iframes
	recursiveAddEventListener: function(node, event, handler, blockP) {
	   // Add handler to current node
	   node.addEventListener(event, handler, blockP);
	   var iframes = node.getElementsByTagName("iframe");
	   for(var i=0; i<iframes.length; i++) {
	     recursiveAddEventListener(iframes[i].contentDocument,event,handler,blockP);
	   }
	
	   var frames = node.getElementsByTagName("frame");
	   for(var j=0; j<frames.length; j++) {
	      recursiveAddEventListener(frames[j].contentDocument,event,handler,blockP);
	   }  
	   
	   return;
	},
	
	// Is true if the DOM node "attribute" has an aaa state attribute namespace
	isStateAttribute: function(attribute) { 
	   if(attribute.name.substring(0, 5) == 'aria-')  { // attribute.namespaceURI == "http://www.w3.org/2005/07/aaa" || 
	      return(true);
	   }
	   return(false);
	},
	
	// Returns an array of aaaStateObjects corresponding to all states in the given node
	// 11/5/07 SMF initializes state object's: supportedState, validAttributeValue, invalidIDREF, emptyIDREF, toomanyIDREF
	getStateArray: function(node, role) { 
	 	try {
		   states = new Array();
		   var rolLoc;
		
		   for(var i=0; i< node.attributes.length; i++ ) {
		      if( isStateAttribute(node.attributes[i]) ) {
			  	var stateObj = newaaaStateObject(node.attributes[i]);
				
		 	  	// 10/15/07 SMF Mark any nodes that are not supportedState according to http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#
		        if ((rolLoc = getRoleLocation(role)) != -1) {
					if (isSupportedState(rolLoc, stateObj.state) == false) {
			            stateObj.supportedState = false;
			         }
				}
				
				//11/1/07 SMF Mark any node that has an invalid value according to aaa-qname.dtd 
				if (stateObj.supportedState == true) {
					try {
						stateObj.validAttributeValue = AE.validStateValuesArray.validNameValuePair(stateObj.state, node.attributes[i].value);
						if (stateObj.validAttributeValue == null) { // check the properties
							var isIDREF = AE.validPropertyValuesArray.validNameValuePair(stateObj.state, "IDREF");
							var isIDREFPlural = AE.validPropertyValuesArray.validNameValuePair(stateObj.state, "IDREFS");
	
							if (isIDREF == true || isIDREFPlural == true) { // make sure the id(s) exists
								if (isIDREFPlural != true) { // make sure only one id has been specified
									var targetArray = node.attributes[i].value.split(" ");
									if (targetArray.length > 1) {
										stateObj.toomanyIDREF = true;
									}
								}
								if (cleanSpaces(node.attributes[i].value) == '') {
									stateObj.emptyIDREF = true;	
								}
								else {
									var returnArray = getTextAssociatedWithIDs(AE.mydoc, node.attributes[i].value, (isIDREFPlural == false));
									stateObj.invalidIDREF = returnArray[1];
								}
							}
						}
					}
					catch(e) { //validStateValuesArray & validPropertyValuesArray are defined in listofdhtml and are not needed in listofdhtmllog
						accessext_consoleDump('validStateValuesArray: ' + e);
					}	 		
		        }
				states[states.length] = stateObj;
		      }
		   }
		   return states;	
		} catch (ex) {
			accessext_consoleDump('getStateArray exception: ', ex);
		}
	},
	
	readRole: function(node) {
		try {
			var role = null;
	        if(node == null) {
				return role;	
			} else {
			    // Get the value of the x2:role attribute
			    //role = node.getAttributeNS("http://www.w3.org/TR/xhtml2", "role");
			    // Quick hack because the above line is a bit too strict most of the time
			    if(!role) {
			       role = node.getAttribute("role");
			    }
			    // KILL NAMESPACE CODE	  
				// Remove any namespace prefixes
				//if (role != null) {
				//    var temp = role.split(":");
				//    role = temp[temp.length-1];
				//}
			}
			return role;	
		} catch (ex) {
			accessext_consoleDump('readRole exception: ', ex);
			accessext_consoleDump('node.nodeName=', node.nodeName);
		}
	},
	
	// Given an array of widget objects, returns the index of the one whose ID matches the given string, 
	// ... else returns null
	findWidgetById: function(widgetarray, widgetID) {
	   for(var i=0; i<widgetarray.length; i++) {
	      if( widgetarray[i].id == widgetID ) {
	         return i;
	      }
	   }
	   return null;
	},
	
	// Returns an XPATH predicate string that will match an attribute "a" whose value matches
	// ... either 'str' exactly or 'something:str'.
	// Used to work around (unsupported?) namespaces in attribute values
	// An XPATH expression that would match every x2:role='wairole:checkbox' in the node and its descendents would be formed as:
	// ... "//*[" + XPathPseudoNamespaceSearchPredicate('x2:role','checkbox') + "]"
	XPathPseudoNamespaceSearchPredicate: function(a,str) {
	   return "contains("+a+",'"+str+"') "
	      + "and substring-after("+a+",'"+str+"')='' "
	      + "and ("
	         + "substring(substring-before("+a+",'"+str+"'),string-length(substring-before("+a+",'"+str+"')),1)=':' "
	         + "or substring(substring-before("+a+",'"+str+"'),string-length(substring-before("+a+",'"+str+"')),1)=''"
	      + ")";
	   
	},
	
	// Uses XPATH to find elements with x2 widget roles, returns an XPath result
	parseWidgetsFromDOM: function(node) {
	    
	   loadWairolesProperties();
	   
	   callback = {
	      run: function() {
	         var wairoles = [];
	         results = myRDF.Match(null, null, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://www.w3.org/2002/07/owl#Class');
	         re = /^.*#(.*)$/;
	         for(var i = 0; i < results.length; i++) {
	            wairoles.push(results[i].subject.match(re)[1]);
	         }
	         return wairoles;
	      }
	   }
	   
		try {
		   var myRDF = new RDF();
		  // var wairoles = myRDF.getRDFURL('http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#', callback);
		   var wairoles = myRDF.getRDFURL('http://codetalks.org/source/tools/Test%20Pages/aria.rdf', callback);
		   
		   var xp = "//*[" + XPathPseudoNamespaceSearchPredicate('@x:role|@x2:role|@role',wairoles[0]) + "]"; 
		   for(var i=1; i<wairoles.length; i++) {
		      xp = xp + " | //*[" + XPathPseudoNamespaceSearchPredicate('@x:role|@x2:role|@role',wairoles[i]) + "]";
		   }
		
		   var xpathResult = node.evaluate(xp, node, accessext_NSResolver, XPathResult.ANY_TYPE,null);
		   return xpathResult;
	 	} catch (ex) {
			accessext_consoleDump('parseWidgetsFromDOM (must be connected to internet) exception: ', ex);
		}
	   
	   // FIXME: BUG: you should check the namespace qualifier on the :checkbox value to make sure that it is truely a widget
	},
	
	getRoleLocation: function(role) {
		for(var i=0; i<AE.wairoleProperties.length; i++) {
			if (AE.wairoleProperties[i].id == role) {return i};
		}
		return -1;
	},
	
	/**
	 * Read the triples from ...GUIRoleTaxonomy into the global variable AE.wairoleProperties, an array of wairoleObject(s).
	 * @param {roleLocation} The name of the role in which we are looking for corresponding state.
	 * @param {state} The state we want to check the validity of for rolelocation.
	 * @return boolean	true is state is listed as a supported state, in ...GUIRoleTaxonomy, of roleLocation or
	 * 		is a supported state of roleLocation's parents. Otherwise, false is returned.
	 * @author Sandy Foltz
	*/
	isSupportedState: function(roleLocation, state) {
		for(var i=0; i< AE.wairoleProperties[roleLocation].supportedStates.length; i++) {
			if (AE.wairoleProperties[roleLocation].supportedStates[i] == state) return true;
		}
		for(var i=0; i< AE.wairoleProperties[roleLocation].subClassOf.length; i++) {
			var nextLoc = getRoleLocation(AE.wairoleProperties[roleLocation].subClassOf[i]);
			if (nextLoc != -1){
				if (isSupportedState(nextLoc, state) == true) return true; 
			}
		}
	
		return false;
	},
	
	/**
	 * Read the triples from ...GUIRoleTaxonomy into the global variable AE.wairoleProperties, an array of wairoleObject(s).
	 * @return nothing.
	 * @author Sandy Foltz
	*/
	loadWairolesProperties: function() {
	   var globalArray = [];
	   callback = {
	      run: function() {
		   	 const GUIRoleTaxonomy = "http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#";
	         var wairoles = [];
	         //Match(triples,subject,predicate,object);
	         results = myRDF.Match(null, null, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://www.w3.org/2002/07/owl#Class');
	 
	         re = /^.*#(.*)$/;
	         for(var i = 0; i < results.length; i++) {
	 		 	rolename = results[i].subject.match(re)[1];
	
				var states = [];
	//			waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#supportedState', null);
				waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/1999/xhtml/vocab#supportedState', null);
				for(var j = 0; j < waiResults.length; j++) {
					states.push(waiResults[j].object.match(re)[1]);
				}
				var subClassOf = [];
				waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/2000/01/rdf-schema#subClassOf', null);
				for(var j = 0; j < waiResults.length; j++) {
					try {
						subClassOf.push(waiResults[j].object.match(re)[1]);
					}catch (e) {
						subClassOf.push(waiResults[j].object);
					}
				}
				var mustContain = [];
				//waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#mustContain', null);
				waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/1999/xhtml/vocab#mustContain', null);			
				for(var j = 0; j < waiResults.length; j++) {
					mustContain.push(waiResults[j].object.match(re)[1]);
				}
				var relatedConcept = [];
				// 1/19/9 SMF none in the file
				//waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#relatedConcept', null);
				//for(var j = 0; j < waiResults.length; j++) {
				//	relatedConcept.push(waiResults[j].object.match(re)[1]);
				//} 
				var description = '';
				//'http://dublincore.org/2003/03/24/dces#description'
				waiResults = myRDF.Match(null, GUIRoleTaxonomy + rolename, 'http://purl.org/dc/elements/1.1/#description', null);
				for(var j = 0; j < waiResults.length; j++) {
					description = cleanSpaces(waiResults[j].object);
				}
			 	wairoles.push(new wairoleObject(rolename, states, subClassOf, mustContain, relatedConcept, description));
	        }
	         return wairoles;
	      }
	   }
	   
	   var myRDF = new RDF();
	//   AE.wairoleProperties = myRDF.getRDFURL('http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#', callback);
		 AE.wairoleProperties = myRDF.getRDFURL("http://codetalks.org/source/tools/Test%20Pages/aria.rdf", callback);
	},
	
	ParseDocumentForWidget: function(node, loadArray) {
	   var xpathResult = parseWidgetsFromDOM(node); 
	
	   var r = xpathResult.iterateNext();
	
	   while (r) {
	      loadArray[loadArray.length] = new widgetObject(loadArray.length + 1, r);
	      r = xpathResult.iterateNext();
	   }
	}
});
}});